// Copyright (c) 2024 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package statereaderioeither provides a functional programming abstraction that combines
// four powerful concepts: State, Reader, IO, and Either monads.
//
// # StateReaderIOEither
//
// StateReaderIOEither[S, R, E, A] represents a computation that:
//   - Manages state of type S (State)
//   - Depends on some context/environment of type R (Reader)
//   - Performs side effects (IO)
//   - Can fail with an error of type E or succeed with a value of type A (Either)
//
// The type is defined as:
//
//	StateReaderIOEither[S, R, E, A] = Reader[S, ReaderIOEither[R, E, Pair[S, A]]]
//
// This is particularly useful for:
//   - Stateful computations with dependency injection
//   - Error handling in effectful computations with state
//   - Composing operations that need access to shared configuration, manage state, and can fail
//
// # Core Operations
//
// Construction:
//   - Of/Right: Create a successful computation with a value
//   - Left: Create a failed computation with an error
//   - FromState: Lift a State into StateReaderIOEither
//   - FromReader: Lift a Reader into StateReaderIOEither
//   - FromIO: Lift an IO into StateReaderIOEither
//   - FromEither: Lift an Either into StateReaderIOEither
//   - FromIOEither: Lift an IOEither into StateReaderIOEither
//   - FromReaderEither: Lift a ReaderEither into StateReaderIOEither
//   - FromReaderIOEither: Lift a ReaderIOEither into StateReaderIOEither
//
// Transformation:
//   - Map: Transform the success value
//   - Chain: Sequence dependent computations (monadic bind)
//   - Flatten: Flatten nested StateReaderIOEither
//
// Combination:
//   - Ap: Apply a function in a context to a value in a context
//
// Context Access:
//   - Asks: Get a value derived from the context
//   - Local: Run a computation with a modified context
//
// Kleisli Arrows:
//   - FromEitherK: Lift an Either-returning function to a Kleisli arrow
//   - FromIOK: Lift an IO-returning function to a Kleisli arrow
//   - FromIOEitherK: Lift an IOEither-returning function to a Kleisli arrow
//   - FromReaderIOEitherK: Lift a ReaderIOEither-returning function to a Kleisli arrow
//   - ChainEitherK: Chain with an Either-returning function
//   - ChainIOEitherK: Chain with an IOEither-returning function
//   - ChainReaderIOEitherK: Chain with a ReaderIOEither-returning function
//
// Do Notation (Monadic Composition):
//   - Do: Start a do-notation chain
//   - Bind: Bind a value from a computation
//   - BindTo: Bind a value to a simple constructor
//   - Let: Compute a derived value
//   - LetTo: Set a constant value
//   - ApS: Apply in sequence (for applicative composition)
//   - BindL/ApSL/LetL/LetToL: Lens-based variants for working with nested structures
//
// # Example Usage
//
//	type Config struct {
//	    DatabaseURL string
//	    MaxRetries  int
//	}
//
//	type AppState struct {
//	    RequestCount int
//	    LastError    error
//	}
//
//	// A computation that manages state, depends on config, performs IO, and can fail
//	func processRequest(data string) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
//	    return func(state AppState) readerioeither.ReaderIOEither[Config, error, pair.Pair[AppState, string]] {
//	        return func(cfg Config) ioeither.IOEither[error, pair.Pair[AppState, string]] {
//	            return func() either.Either[error, pair.Pair[AppState, string]] {
//	                // Use cfg.DatabaseURL and cfg.MaxRetries
//	                // Update state.RequestCount
//	                // Perform IO operations
//	                // Return either.Right(pair.MakePair(newState, result)) or either.Left(err)
//	                newState := AppState{RequestCount: state.RequestCount + 1}
//	                return either.Right(pair.MakePair(newState, "processed: " + data))
//	            }
//	        }
//	    }
//	}
//
//	// Compose operations using do-notation
//	result := function.Pipe3(
//	    statereaderioeither.Do[AppState, Config, error](State{}),
//	    statereaderioeither.Bind(
//	        func(result string) func(State) State { return func(s State) State { return State{result: result} } },
//	        func(s State) statereaderioeither.StateReaderIOEither[AppState, Config, error, string] {
//	            return processRequest(s.input)
//	        },
//	    ),
//	    statereaderioeither.Map[AppState, Config, error](func(s State) string { return s.result }),
//	)
//
//	// Execute with initial state and config
//	initialState := AppState{RequestCount: 0}
//	config := Config{DatabaseURL: "postgres://localhost", MaxRetries: 3}
//	outcome := result(initialState)(config)() // Returns either.Either[error, pair.Pair[AppState, string]]
//
// # Monad Laws
//
// StateReaderIOEither satisfies the monad laws:
//   - Left Identity: Of(a) >>= f ≡ f(a)
//   - Right Identity: m >>= Of ≡ m
//   - Associativity: (m >>= f) >>= g ≡ m >>= (x => f(x) >>= g)
//
// These laws are verified in the testing subpackage.
package statereaderioeither
